/**************************************************************************************************
* Title: SumStackAllocator.h
* Author: Gael Huber
* Description: Standard stack-based allocator.
**************************************************************************************************/
#ifndef __SUMSTACKALLOCATOR_H__
#define __SUMSTACKALLOCATOR_H__

#include <assert.h>
#include <malloc.h>
#include "SumDataTypes.h"

class StackAllocator {
public:
	// Constructs a stack allocator with the given total size in bytes.
	inline explicit StackAllocator(const size_t stackSize_bytes)
	{
		// Allocate bytes and assign the returned value to bottom
		stackBottom = (Marker) malloc(stackSize_bytes) - stackSize_bytes;
		stackTop = stackBottom;
		stackMax = stackBottom + stackSize_bytes;
	}

	// Destructor
	inline ~StackAllocator(void)
	{ }

	// Allocates a new block of the given size from stack top.
	inline void* const alloc(const size_t size_bytes)
	{
		// In reality, we want to increment the stack top to allocate the proper number of bytes, but 
		// since memory is allocated in a increasing order, the return value must be the current top of 
		// the stack to ensure that the data from x to x+size is filled.
		Marker newTop = stackTop + size_bytes;
		assert(newTop <= stackMax);
		stackTop = newTop;
		return (void*) (stackTop - size_bytes);
	}

	// Returns a marker to the current stack top
	inline const Marker getMarker(void) const
	{
		return stackTop;
	}

	// Rolls the stack back to a previous marker.
	inline void freeToMarker(const Marker marker)
	{
		// Ensure that we aren't clearing past the bottom of the stack
		assert(marker >= stackBottom);
		stackTop = marker;
	}

	// Rolls the stack back to a previous marker, but takes in a void* and does the explicit 
	// conversion within the bounds of the function.
	inline void freeToMarker(const void* marker)
	{
		// Explicit conversion
		Marker m = (Marker)marker;
		
		// Ensure that we aren't clearing past the bottom of the stack
		assert(m >= stackBottom);
		stackTop = m;
	}

	// Clears the entire stack (rolls the stack back to zero).
	inline void clear(void)
	{
		stackTop = stackBottom;
	}

private:
	Marker stackTop;	// Marks the current top of the stack
	Marker stackBottom;	// Marks the bottom of the stack
	Marker stackMax;	// Marks the furthest extent of the stack (ensure that allocations cannot be made
		// beyond this point
};

// Overload new and delete for the benefit of the stack allocator

// Overload new for syntactic ease-of-use (permits regular initialization/allocation of objects)
static inline void* operator new (size_t size, StackAllocator& sa)
{
	return sa.alloc(size);
}

// Overload new[] for syntactic ease-of-use (permits regular initialization/allocation of arrays)
static inline void* operator new[] (size_t size, StackAllocator& sa)
{
	return sa.alloc(size);
}

// Overload delete for syntactic ease-of-use (permits regular deletion of objects)
static inline void operator delete (void* mem, StackAllocator& sa)
{
	sa.freeToMarker((Marker)mem);
}

// Overload delete[] for syntactic ease-of-use (permits regular deletion of array objects
static inline void operator delete[] (void* mem, StackAllocator& sa)
{
	sa.freeToMarker((Marker)mem);
}

#endif